//
//  QRCodeManager.swift
//  EyesCare
//
//  Created by TT on 2018/12/20.
//  Copyright © 2018年 qdsg. All rights reserved.
//

import UIKit
import AVFoundation
import CoreImage

class QRCodeManager: NSObject {
    
    private let session = AVCaptureSession()
    private var input: AVCaptureDeviceInput?
    private var output: AVCaptureMetadataOutput!
    private var scanView: QRCodeScanView!
    
    private var lightObserver: ((_ dimmed: Bool, _ torchOn: Bool) -> ())?
    private var callback: ((_ result: String?) -> ())?
    private var isLightObserverHasCalled = false
    private var autoStop = false
    
    init(scanView: QRCodeScanView, completion: (() -> ())? = nil) {
        super.init()
        self.scanView = scanView
        self.scanView.delegate = self
        setupNotification()
        setupSession(completion)
    }
    
    /// 开始扫描
    func startScanning(callback: @escaping (_ result: String?) -> (), autoStop: Bool = false) {
        self.callback = callback
        self.autoStop = autoStop
        startScanning()
    }
    
    /// 停止扫描
    func stopScanning() {
        if session.isRunning {
            session.stopRunning()
            scanView.stopScanning()
        }
        switchTorch(on: false)
        resetZoomFactor()
    }
    
    /// 相册
    func presentPhotoLibrary(withRootVc rooter: UIViewController, callback: @escaping (_ result: String?) -> ()) {
        self.callback = callback
        let imgPicker = UIImagePickerController()
        imgPicker.sourceType = .photoLibrary
        imgPicker.delegate = self
        rooter.present(imgPicker, animated: true, completion: nil)
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

// MARK: - 设置
extension QRCodeManager {
    /// 设置通知
    private func setupNotification() {
        NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(applicationWillEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
    }
    
    /// 异步初始化session
    private func setupSession(_ completion: (() -> ())?) {
        DispatchQueue.global().async {
            guard let device = AVCaptureDevice.default(for: .video) else { return }
            do {
                self.input = try AVCaptureDeviceInput(device: device)
            } catch let error {
                print("input: \(error)")
                return
            }
            self.output = AVCaptureMetadataOutput()
            self.output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
            
            self.session.sessionPreset = .high
            
            if self.session.canAddInput(self.input!) {
                self.session.addInput(self.input!)
            }
            if self.session.canAddOutput(self.output) {
                self.session.addOutput(self.output)
                if self.output.availableMetadataObjectTypes.contains(.qr) &&
                    self.output.availableMetadataObjectTypes.contains(.code128) &&
                    self.output.availableMetadataObjectTypes.contains(.ean13) {
                    self.output.metadataObjectTypes = [.qr, .code128, .ean13]
                }
            }
            // 持续对焦
            if (device.isFocusPointOfInterestSupported &&
                device.isFocusModeSupported(.continuousAutoFocus) ) {
                do {
                    try self.input?.device.lockForConfiguration()
                    self.input?.device.focusMode = .continuousAutoFocus
                    self.input?.device.unlockForConfiguration()
                } catch let error as NSError {
                    print("device.lockForConfiguration(): \(error)")
                }
            }
            
            /// 回主线程刷新UI
            DispatchQueue.main.async {
                // 捕捉图层
                let previewLayer = AVCaptureVideoPreviewLayer(session: self.session)
                previewLayer.frame = self.scanView.layer.bounds
                previewLayer.videoGravity = .resizeAspectFill
                self.scanView.layer.insertSublayer(previewLayer, at: 0)
                
                // 设置扫码区域
                let rectFrame = self.scanView.rectFrame
                if !rectFrame.equalTo(.zero) {
                    let y = rectFrame.origin.y / self.scanView.bounds.height
                    let x = (self.scanView.bounds.width - rectFrame.origin.x - rectFrame.size.width) / self.scanView.bounds.width
                    let h = rectFrame.size.height / self.scanView.bounds.height
                    let w = rectFrame.size.width / self.scanView.bounds.width
                    self.output.rectOfInterest = CGRect(x: y, y: x, width: h, height: w)
                }
                
                // 缩放手势
                let pinch = UIPinchGestureRecognizer(target: self, action: #selector(self.pinch(_:)))
                self.scanView.addGestureRecognizer(pinch)
                
                self.scanView.stopIndicating()
                
                if completion != nil {
                    completion!()
                }
            }
        }
    }
}

// MARK: - 扫描
extension QRCodeManager {
    /// 开始扫描
    private func startScanning() {
        if !session.isRunning {
            session.startRunning()
            scanView.startScanning()
        }
        
        observerLightStatus { [weak self] (dimmed, torchOn) in
            if dimmed || torchOn {
//                self?.scanView.stopScanning()
                self?.scanView.showTorchSwitch()
            } else {
//                self?.scanView.startScanning()
                self?.scanView.hideTorchSwitch()
            }
        }
    }
}

// MARK: - 事件
extension QRCodeManager {
    /// app将进入前台
    @objc private func applicationWillEnterForeground(_ noti: Notification) {
        startScanning()
    }
    
    /// app已经进入后台
    @objc private func applicationDidEnterBackground(_ noti: Notification) {
        stopScanning()
    }
    
    @objc private func pinch(_ gesture: UIPinchGestureRecognizer) {
        guard let device = AVCaptureDevice.default(for: .video) else { return }
        var minZoomFactor: CGFloat = 1.0
        var maxZoomFactor = device.activeFormat.videoMaxZoomFactor
        if #available(iOS 11.0, *) {
            minZoomFactor = device.minAvailableVideoZoomFactor
            maxZoomFactor = device.maxAvailableVideoZoomFactor
        }
        
        var lastZoomFactor: CGFloat = 1.0
        if gesture.state == .began {
            lastZoomFactor = device.videoZoomFactor
        } else if gesture.state == .changed {
            var zoomFactor = lastZoomFactor * gesture.scale
            zoomFactor = CGFloat(fmaxf(fminf(Float(zoomFactor), Float(maxZoomFactor)), Float(minZoomFactor)))
            try? device.lockForConfiguration()
            device.videoZoomFactor = zoomFactor
            device.unlockForConfiguration()
        }
    }
}

// MARK: - QRCodeScanViewDelegate
extension QRCodeManager: QRCodeScanViewDelegate {
    func scanView(scanView: QRCodeScanView, didClickedTorchSwitch sender: UIButton) {
        sender.isSelected = !sender.isSelected
        switchTorch(on: sender.isSelected)
        isLightObserverHasCalled = sender.isSelected
    }
}

// MARK: - Helpers
extension QRCodeManager {
    private func switchTorch(on: Bool) {
        guard let device = AVCaptureDevice.default(for: .video) else { return }
        if device.hasFlash && device.hasTorch {
            try? device.lockForConfiguration()
            device.torchMode = on ? .on : .off
            device.flashMode = on ? .on : .off
            device.unlockForConfiguration()
        }
    }
    
    /// 打开/关闭手电筒
    private func observerLightStatus(lightObserver: @escaping (_ dimmed: Bool, _ torchOn: Bool) -> ()) {
        self.lightObserver = lightObserver
        let lightOutput = AVCaptureVideoDataOutput()
        lightOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main)
        if session.canAddOutput(lightOutput) {
            session.addOutput(lightOutput)
        }
    }
    
    /// 重置
    private func resetZoomFactor() {
        guard let device = AVCaptureDevice.default(for: .video) else { return }
        try? device.lockForConfiguration()
        device.videoZoomFactor = 1.0
        device.unlockForConfiguration()
    }
    
    /// 处理扫描结果
    private func handle(codeValue value: String) {
        if autoStop {
            stopScanning()
        }
        if let callback = callback {
            callback(value)
        }
    }
}

// MARK: - AVCaptureMetadataOutputObjectsDelegate
extension QRCodeManager: AVCaptureMetadataOutputObjectsDelegate {
    func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
        if metadataObjects.count > 0 {
            if let metadataObject = metadataObjects[0] as? AVMetadataMachineReadableCodeObject {
                if let stringValue = metadataObject.stringValue {
                    handle(codeValue: stringValue)
                }
            }
        }
    }
}

// MARK: - AVCaptureVideoDataOutputSampleBufferDelegate
extension QRCodeManager: AVCaptureVideoDataOutputSampleBufferDelegate {
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        let metadataDicRef = CMCopyDictionaryOfAttachments(allocator: nil, target: sampleBuffer, attachmentMode: kCMAttachmentMode_ShouldPropagate)
        guard let metadata = metadataDicRef as? [AnyHashable: Any] else { return }
        guard let exifMetadata = metadata[kCGImagePropertyExifDictionary as String] as? [AnyHashable: Any],
            let brightness = exifMetadata[kCGImagePropertyExifBrightnessValue as String] as? NSNumber else {
                return
        }
        // 亮度值
        let brightnessValue = brightness.floatValue
        guard let device = AVCaptureDevice.default(for: .video) else { return }
        let torchOn = device.torchMode == .on
        let dimmed = brightnessValue < 1.0
        var lastDimmed = false
        if let lightObserver = lightObserver {
            if !isLightObserverHasCalled {
                lightObserver(dimmed, torchOn)
                isLightObserverHasCalled = true
                lastDimmed = dimmed
            }
            if dimmed != lastDimmed {
                lightObserver(dimmed, torchOn)
                lastDimmed = dimmed
            }
        }
    }
}

// MARK: - UIImagePickerControllerDelegate
extension QRCodeManager: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        guard let pickerImg = info[.originalImage] as? UIImage else { return }
        guard let detectorImg = CIImage(image: pickerImg) else { return }
        // 创建过滤器
        let detector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: nil)
        guard let result = detector?.features(in: detectorImg).first as? CIQRCodeFeature else { return }
        picker.dismiss(animated: true) {
            if let messageString = result.messageString {
                self.handle(codeValue: messageString)
            }
        }
    }
}
